/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.auth.util;

import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTException;
import com.je.auth.AuthTokenDao;
import com.je.auth.check.util.FoxUtil;
import com.je.auth.check.exception.NotLoginException;
import com.je.auth.check.exception.TokenException;
import java.util.Map;

/**
 * jwt操作工具类封装
 *
 * @author kong
 */
public class JwtUtil {

    /**
     * key：账号id
     */
    public static final String LOGIN_ID = "loginId";

    /**
     * key：登录设备
     */
    public static final String DEVICE = "device";

    /**
     * key：有效截止期 (时间戳)
     */
    public static final String EFF = "eff";

    /**
     * 当有效期被设为此值时，代表永不过期
     */
    public static final long NEVER_EXPIRE = AuthTokenDao.NEVER_EXPIRE;

    // ------ 创建

    /**
     * 创建 jwt （简单方式）
     *
     * @param loginId   账号id
     * @param extraData 扩展数据
     * @param keyt      秘钥
     * @return jwt-token
     */
    public static String createToken(Object loginId, Map<String, Object> extraData, String keyt) {

        // 秘钥不可以为空
        TokenException.throwByNull(keyt, "请配置jwt秘钥");

        // 构建
        String token = JWT.create()
                .setPayload(LOGIN_ID, loginId)
                // 混入随机字符
                .setPayload("rn", FoxUtil.getRandomString(32))
                .addPayloads(extraData)
                .setKey(keyt.getBytes())
                .sign();

        // 返回
        return token;
    }

    /**
     * 创建 jwt （全参数方式）
     *
     * @param loginId   账号id
     * @param device    设备标识
     * @param timeout   token有效期 (单位 秒)
     * @param extraData 扩展数据
     * @param keyt      秘钥
     * @return jwt-token
     */
    public static String createToken(Object loginId, String device,
                                     long timeout, Map<String, Object> extraData, String keyt) {

        // 秘钥不可以为空
        TokenException.throwByNull(keyt, "请配置jwt秘钥");

        // 计算有效期
        long effTime = timeout;
        if (timeout != NEVER_EXPIRE) {
            effTime = timeout * 1000 + System.currentTimeMillis();
        }

        // 创建
        JWT jwt = JWT.create()
                .setPayload(LOGIN_ID, loginId)
                .setPayload(DEVICE, device)
                .setPayload(EFF, effTime)
                .addPayloads(extraData);

        // 返回
        return jwt.setKey(keyt.getBytes()).sign();
    }

    // ------ 解析

    /**
     * jwt 解析（校验签名和有效期）
     *
     * @param token Jwt-Token值
     * @param keyt  秘钥
     * @return 解析后的jwt 对象
     */
    public static JWT parseToken(String token, String keyt) {

        // 如果token为null
        if (token == null) {
            throw NotLoginException.newInstance(null, NotLoginException.NOT_TOKEN);
        }

        // 解析
        JWT jwt = null;
        try {
            jwt = JWT.of(token);
        } catch (JWTException e) {
            // 解析失败
            throw NotLoginException.newInstance(NotLoginException.INVALID_TOKEN, token);
        }
        JSONObject payloads = jwt.getPayloads();

        // 校验 Token 签名
        boolean verify = jwt.setKey(keyt.getBytes()).verify();
        if (verify == false) {
            throw NotLoginException.newInstance(NotLoginException.INVALID_TOKEN, token);
        }
        ;

        // 校验 Token 有效期
        Long effTime = payloads.getLong(EFF, 0L);
        if (effTime != NEVER_EXPIRE) {
            if (effTime == null || effTime < System.currentTimeMillis()) {
                throw NotLoginException.newInstance(NotLoginException.TOKEN_TIMEOUT, token);
            }
        }

        // 返回 
        return jwt;
    }

    /**
     * 获取 jwt 数据载荷 （校验签名和有效期）
     *
     * @param token token值
     * @param keyt  秘钥
     * @return 载荷
     */
    public static JSONObject getPayloads(String token, String keyt) {
        return parseToken(token, keyt).getPayloads();
    }

    /**
     * 获取 jwt 数据载荷 （只校验签名，不校验有效期）
     *
     * @param token token值
     * @param keyt  秘钥
     * @return 载荷
     */
    public static JSONObject getPayloadsNotCheck(String token, String keyt) {
        try {
            JWT jwt = JWT.of(token);
            JSONObject payloads = jwt.getPayloads();

            // 校验 Token 签名
            boolean verify = jwt.setKey(keyt.getBytes()).verify();
            if (verify == false) {
                throw NotLoginException.newInstance(NotLoginException.INVALID_TOKEN, token);
            }
            return payloads;
        } catch (JWTException e) {
            return new JSONObject();
        }
    }

    /**
     * 获取 jwt 代表的账号id
     *
     * @param token Token值
     * @param keyt  秘钥
     * @return 值
     */
    public static Object getLoginId(String token, String keyt) {
        return getPayloads(token, keyt).get(LOGIN_ID);
    }

    /**
     * 获取 jwt 代表的账号id (未登录时返回null)
     *
     * @param token Token值
     * @param keyt  秘钥
     * @return 值
     */
    public static Object getLoginIdOrNull(String token, String keyt) {
        try {
            return getPayloads(token, keyt).get(LOGIN_ID);
        } catch (NotLoginException e) {
            return null;
        }
    }

    /**
     * 获取 jwt 剩余有效期
     *
     * @param token JwtToken值
     * @param keyt  秘钥
     * @return 值
     */
    public static long getTimeout(String token, String keyt) {

        // 如果token为null
        if (token == null) {
            return AuthTokenDao.NOT_VALUE_EXPIRE;
        }

        // 取出数据
        JWT jwt = null;
        try {
            jwt = JWT.of(token);
        } catch (JWTException e) {
            // 解析失败
            return AuthTokenDao.NOT_VALUE_EXPIRE;
        }
        JSONObject payloads = jwt.getPayloads();

        // 如果签名无效
        boolean verify = jwt.setKey(keyt.getBytes()).verify();
        if (verify == false) {
            return AuthTokenDao.NOT_VALUE_EXPIRE;
        }
        ;

        // 如果被设置为：永不过期
        Long effTime = payloads.get(EFF, Long.class);
        if (effTime == NEVER_EXPIRE) {
            return NEVER_EXPIRE;
        }
        // 如果已经超时
        if (effTime == null || effTime < System.currentTimeMillis()) {
            return AuthTokenDao.NOT_VALUE_EXPIRE;
        }

        // 计算timeout (转化为以秒为单位的有效时间)
        return (effTime - System.currentTimeMillis()) / 1000;
    }

}
